package com.ydlclass.bootstrap;

import com.ydlclass.codec.YrpcMessageDecoder;
import com.ydlclass.codec.YrpcMessageEncoder;
import com.ydlclass.compress.Compress;
import com.ydlclass.configuration.YrpcConfiguration;
import com.ydlclass.configuration.YrpcConfigurationHolder;
import com.ydlclass.factory.proxy.YrpcClientProxy;
import com.ydlclass.handler.ProcessYrpcMessageHandler;
import com.ydlclass.loadbalance.LoadBalancer;
import com.ydlclass.registry.Registry;
import com.ydlclass.serialization.Serializer;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.util.concurrent.CompletableFuture;

/**
 * @author it楠老师
 * @createTime 2023-03-21
 * 该类是netty客户端实现，客户端的主要功能如下：
 * 1、从注册中心抓取服务列表，并实现loadBalance
 * 2、使用代理模式为访问接口生成代理
 */
@Slf4j
public class ClientBootstrap {

    private Bootstrap bootstrap;
    private NioEventLoopGroup group;
    private final YrpcConfiguration yrpcConfiguration;
    private static final ClientBootstrap clientBootstrap = new ClientBootstrap();

    // 私有化构造器，实现单例
    private ClientBootstrap() {
        // 配置默认的上下文,并缓存
        YrpcConfiguration.Builder builder = new YrpcConfiguration.Builder();
        yrpcConfiguration = builder.build();
        YrpcConfigurationHolder.set(yrpcConfiguration);

        // 创建一个netty客户端
        initNettyClient();
    }

    /**
     * 获取单例实例
     * @return 单例实例
     */
    public static ClientBootstrap getInstance() {
        return clientBootstrap;
    }

    /**
     * 初始化netty客户端，并添加pipeline
     */
    private void initNettyClient(){
        bootstrap = new Bootstrap();
        bootstrap.channel(NioSocketChannel.class);
        group = new NioEventLoopGroup();
        bootstrap.group(group);
        bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch){
                LoggingHandler loggingHandler = new LoggingHandler(LogLevel.DEBUG);
                ch.pipeline()
                        .addLast(loggingHandler)
                        .addLast(new YrpcMessageDecoder())
                        .addLast(new YrpcMessageEncoder())
                        .addLast(new ProcessYrpcMessageHandler());
            }
        });
    }

    /**
     * 获取注册中心中的服务
     * @param serviceName 服务名称
     */


    /**
     * 设置注册中心
     * @param registry 注册中心实例
     * @return this
     */
    public ClientBootstrap registry(Registry registry) {
        this.yrpcConfiguration.setRegistry(registry);
        return this;
    }


    /**
     * 配置客户端负载均衡
     * @param loadBalance 负载均衡策略
     * @return this
     */
    public ClientBootstrap loadBalance(LoadBalancer loadBalance) {
        yrpcConfiguration.setLoadBalance(loadBalance);
        return this;
    }

    /**
     * 配置默认的压缩方式，默认不开启
     * @param compress 压缩方式
     * @return this
     */
    public ClientBootstrap compress(Compress compress) {
        yrpcConfiguration.setCompress(compress);
        return this;
    }

    /**
     * 配置序列化方式，默认jdk
     * @param serializer 序列化方式
     * @return this
     */
    public ClientBootstrap serialization(Serializer serializer) {
        yrpcConfiguration.setSerializer(serializer);
        return this;
    }

    /**
     * 为客户端生成代理，生成的代理会包保存在yrpcClientProxy，通过get方法获取
     * @param yrpcClientProxy 客户端的代理工厂
     * @return this
     */
    public ClientBootstrap clientProxy(YrpcClientProxy<?> yrpcClientProxy) {
        yrpcClientProxy.setYrpcConfiguration(yrpcConfiguration);
        Object proxyInstance = Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class[]{yrpcClientProxy.getInterfaceClass()},
                yrpcClientProxy
        );
        yrpcClientProxy.setObject(proxyInstance);
        return this;
    }



    /**
     * 获取一个连接的方法（A method to get a connection）。
     * @param host 主机名（ip）
     * @param port 端口
     * @return 连接实例
     */
    public Channel connect(String host,int port){
        InetSocketAddress inetSocketAddress = new InetSocketAddress(host,port);
        return this.connect(inetSocketAddress);
    }

    @SneakyThrows
    public Channel connect(InetSocketAddress inetSocketAddress) {
        if(inetSocketAddress == null){
            throw new IllegalArgumentException("You are trying to connect to a remote service that does not exist.");
        }
        // 阻塞获取一个连接
        final CompletableFuture<Channel> completableFuture = new CompletableFuture<>();
        String address = inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort();
        bootstrap.connect(inetSocketAddress).addListener((ChannelFutureListener) future -> {
            if (future.isSuccess()) {
                log.info("--> The client has connected [{}] successful!", address);
                completableFuture.complete(future.channel());
            } else {
                throw new IllegalStateException("You tried to connect to "+ address +", but it is not connected.");
            }
        });
        return completableFuture.get();
    }


    /**
     * 关闭客户端
     */
    public void close() {
        group.shutdownGracefully();
    }

}
